package com.mla.fastdfs.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.util.StringUtil;
import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.exception.FdfsIOException;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import com.mla.fastdfs.commom.Result;
import com.mla.fastdfs.commom.UpLoadConstant;
import com.mla.fastdfs.entity.MultipartFileParam;
import com.mla.fastdfs.entity.ZipMultipartFileParam;
import com.mla.fastdfs.entity.ZipPartInfo;
import com.mla.fastdfs.pojo.UploadUpInfo;
import com.mla.fastdfs.pojo.User;
import com.mla.fastdfs.service.IUploadUpInfoService;
import com.mla.fastdfs.service.MyAsyncService;
import com.mla.fastdfs.service.UserService;
import com.mla.fastdfs.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.Logger;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.SocketTimeoutException;
import java.util.*;


/**
 * @author ：
 * @date ：Created in 2020/2/18 20:36
 * @description：
 */
@Slf4j
@RestController
public class FileController {

    //视频缓存时间,秒:1天
    private final Integer cacheTime = 60 * 60 * 24;
    @Autowired
    AppendFileStorageClient appendFileStorageClient;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    FastDFSClientUtil fastDFSClientUtil;
    @Autowired
    private ChunkUploadUtil chunkUploadUtil;
    @Autowired
    private IUploadUpInfoService uploadUpInfoService;
    @Autowired
    private UserService userService;
    @Autowired
    private MyAsyncService myAsyncService;

    @PostMapping("/upload")
    public Map fdfsUpload(@RequestParam("file") MultipartFile file, String aToken, String userId, String anotherName, Integer groupId, HttpServletRequest request) {
        Map<String, String> result = new HashMap<>();
        result.put("code", "200");
        try {
            if (groupId == null) {
                groupId = 1;
            }
            if (StringUtil.isEmpty(userId) && StringUtil.isEmpty(aToken)) {
                userId = "1";
            }
            String fileUrl = fastDFSClientUtil.uploadFile(file, aToken, userId, groupId, anotherName, request);
            result.put("path", fileUrl);
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
            result.put("code", "500");
            result.put("msg", "上传失败!");
            return result;
        }
        return result;
    }

    @PostMapping(value = "/chunkUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Map chunkUpload(MultipartFileParam multipartFileParam) {
        String fileUrl = "fail";
        String path = "F:/upload/";
        Map<String, String> map = new HashMap<>();
        map.put("path", fileUrl);
        try {
            fileUrl = chunkUploadUtil.chunkUploadByMappedByteBuffer(multipartFileParam, path);
            map.put("path", fileUrl);
        } catch (Exception e) {
            e.printStackTrace();
            return map;
        }
        return map;
    }

    /**
     * 分片断点续传
     *
     * @param multipartFileParam
     * @param response
     * @return
     */
    @PostMapping(value = "/fastDfsChunkUpload")
    public Map fastDfsChunkUpload(MultipartFileParam multipartFileParam, HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> map = new HashMap<>();
        map.put("code", "500");
        Assert.notNull(multipartFileParam.getFile(), "上传内容不能为空");
        Assert.notNull(multipartFileParam.getIdentifier(), "缺少上传唯一标识");
        Assert.notNull(multipartFileParam.getFileName(), "缺少上传文件名");
        Assert.notNull(multipartFileParam.getAnotherName(), "请输入文件名称");
        if (multipartFileParam.getGroupId() == null) {
            multipartFileParam.setGroupId(1);
        }
        if (StringUtil.isEmpty(multipartFileParam.getUserId()) && StringUtil.isEmpty(multipartFileParam.getAToken())) {
            multipartFileParam.setUserId("1");
        }

        long chunk = multipartFileParam.getChunkNumber();
        long totalChunk = multipartFileParam.getTotalChunks();
        long chunkSize = multipartFileParam.getChunkSize();
        long historyUpload = (chunk - 1) * chunkSize;
        String md5 = multipartFileParam.getIdentifier();
        MultipartFile file = multipartFileParam.getFile();
        String fileName = multipartFileParam.getFileName();
        fileName = fileName.substring(fileName.lastIndexOf(".") + 1);
        StorePath path = null;
        String groundPath = null;
        Integer chunkNum;
        try {
            User user;
            if (multipartFileParam.getUserId() != null)
                user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getId, multipartFileParam.getUserId()));
            else user = userService.getUser(multipartFileParam.getAToken());

            if (user == null) {
                map.put("msg", "用户不存在");
                return map;
            }
            List<UploadUpInfo> upInfoList = uploadUpInfoService.list(new QueryWrapper<UploadUpInfo>().lambda()
                    .isNotNull(UploadUpInfo::getFileUrl).eq(UploadUpInfo::getFlag, multipartFileParam.getIdentifier()));
            if (upInfoList != null && upInfoList.size() > 0) {
                UploadUpInfo upInfo = upInfoList.get(0);

                UploadUpInfo one = uploadUpInfoService.getOne(new QueryWrapper<UploadUpInfo>().lambda()
                        .eq(UploadUpInfo::getFlag, multipartFileParam.getIdentifier())
                        .eq(UploadUpInfo::getGroupId, multipartFileParam.getGroupId())
                        .eq(UploadUpInfo::getUid, user.getId()));
                if (one == null) {
                    upInfo.setId(null);
                    upInfo.setGroupId(multipartFileParam.getGroupId());
                    upInfo.setUid(user.getId());
                    upInfo.setIp(ToolUtil.getLocalIp(request));
                    upInfo.setCreateTime(DateUtil.getStrTime());
                    upInfo.setAnotherName(multipartFileParam.getAnotherName());
                    uploadUpInfoService.save(upInfo);
                } else {
                    upInfo.setAnotherName(multipartFileParam.getAnotherName());
                    one.setCreateTime(DateUtil.getStrTime());
                    uploadUpInfoService.updateById(one);
                }
                map.put("code", "200");
                map.put("msg", "上传成功");
                map.put("path", upInfo.getFileUrl());
                return map;
            }

            chunkNum = (Integer) redisUtil.get(UpLoadConstant.uploadChunkNum + md5);
            //判断之前是不是上传过
            if (chunkNum == null && chunk == 1) {
                path = appendFileStorageClient.uploadAppenderFile(UpLoadConstant.DEFAULT_GROUP, file.getInputStream(), file.getSize(), fileName);
                if (path == null) {
                    map.put("msg", "上传第一个就错了");
                    response.setStatus(500);
                    return map;
                }

                chunkNum = 1;
                redisUtil.set(UpLoadConstant.uploadChunkNum + md5, chunkNum, cacheTime);
                groundPath = path.getPath();
                redisUtil.set(UpLoadConstant.fastDfsPath + md5, groundPath, cacheTime);
                UploadUpInfo info = new UploadUpInfo();
                info.setFileName(multipartFileParam.getFileName())
                        .setFlag(multipartFileParam.getIdentifier())
                        .setPartNum((int) multipartFileParam.getTotalChunks())
                        .setIp(ToolUtil.getLocalIp(request))
                        .setFileSize(multipartFileParam.getFileSize())
                        .setStartTime(DateUtil.getStrTime())
                        .setUid(user.getId())
                        .setGroupId(multipartFileParam.getGroupId())
                        .setCreateTime(DateUtil.getStrTime())
                        .setAnotherName(multipartFileParam.getAnotherName());
                uploadUpInfoService.save(info);
            }
            if (chunkNum == null) {
                return map;
            }
            //上传文件不能跳格上传
            if ((chunk - chunkNum) == 1) {
                groundPath = (String) redisUtil.get(UpLoadConstant.fastDfsPath + md5);
                long size = file.getSize();
                log.info(groundPath + ":" + size + ":" + historyUpload);
                appendFileStorageClient.modifyFile(UpLoadConstant.DEFAULT_GROUP, groundPath, file.getInputStream(),
                        file.getSize(), historyUpload);

                redisUtil.set(UpLoadConstant.uploadChunkNum + md5, ++chunkNum, cacheTime);
            }

            map.put("code", "200");
            if (totalChunk == chunkNum) {
                String pathStr = UpLoadConstant.VISIT_URL + groundPath;
                response.setStatus(200);
                map.put("msg", "上传成功");
                map.put("path", pathStr);
                redisUtil.del(UpLoadConstant.uploadChunkNum + md5);
                redisUtil.del(UpLoadConstant.fastDfsPath + md5);
                UploadUpInfo info = new UploadUpInfo();
                info.setFlag(multipartFileParam.getIdentifier())
                        .setEndTime(DateUtil.getStrTime())
                        .setFileUrl(map.get("path"));

                uploadUpInfoService.update(info, new QueryWrapper<UploadUpInfo>().lambda().eq(UploadUpInfo::getFlag, multipartFileParam.getIdentifier()));
                String suffix = pathStr.substring(pathStr.lastIndexOf("."));
                //如果是压缩文件，则进行解压
                if (ZipUtils.compress.contains(suffix)) {
                    myAsyncService.downloadZipFile(info);
                }
            }
        } catch (FdfsIOException | SocketTimeoutException e) {
            response.setStatus(407);
            map.put("code", "407");
            map.put("msg", "重新发送");
            return map;
        } catch (
                Exception e) {
            e.printStackTrace();
            response.setStatus(500);
            map.put("msg", "upload error");
            return map;
        }
        return map;
    }


    @PostMapping("/deleteFile")
    public Map deleteFile(String url) {
        Map<String, String> result = new HashMap<>();
        if (StringUtil.isEmpty(url)) {
            result.put("code", "500");
            result.put("msg", "文件路径不能为空");
            return result;
        }
        url = url.replace(UpLoadConstant.VISIT_URL, "");

        appendFileStorageClient.deleteFile(UpLoadConstant.DEFAULT_GROUP, url);

        result.put("code", "200");
        result.put("msg", "删除成功");
        return result;
    }

    /**
     * zip文件分片上传
     *
     * @param zipMultipartFile
     * @param request
     * @param response
     * @return
     */
    @PostMapping("/zipPartUpload")
    public Result zipPartUpload(ZipMultipartFileParam zipMultipartFile, HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(zipMultipartFile.getFile(), "上传内容不能为空");
        Assert.notNull(zipMultipartFile.getIdentifier(), "缺少上传唯一标识");
        Assert.notNull(zipMultipartFile.getFileName(), "缺少上传文件名");
        Assert.notNull(zipMultipartFile.getAnotherName(), "请输入文件名称");
        Assert.notNull(zipMultipartFile.getZipType(), "压缩文件类型不能为空");
        try {
            if (!ZipUtils.compress.contains(zipMultipartFile.getZipType())) {
                return Result.error("该压缩类型不支持");
            }
            if (zipMultipartFile.getGroupId() == null) {
                zipMultipartFile.setGroupId(1);
            }
            if (StringUtil.isEmpty(zipMultipartFile.getUserId()) && StringUtil.isEmpty(zipMultipartFile.getAToken())) {
                zipMultipartFile.setUserId("1");
            }

            User user;
            if (zipMultipartFile.getUserId() != null)
                user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getId, zipMultipartFile.getUserId()));
            else user = userService.getUser(zipMultipartFile.getAToken());

            if (user == null) return Result.error("用户不存在");

            //判断是不是上传过的文件
            List<UploadUpInfo> upInfoList = uploadUpInfoService.list(new QueryWrapper<UploadUpInfo>().lambda()
                    .isNotNull(UploadUpInfo::getFileUrl).eq(UploadUpInfo::getFlag, zipMultipartFile.getIdentifier()));
            if (upInfoList != null && upInfoList.size() > 0) {
                UploadUpInfo upInfo = upInfoList.get(0);

                UploadUpInfo one = uploadUpInfoService.getOne(new QueryWrapper<UploadUpInfo>().lambda()
                        .eq(UploadUpInfo::getFlag, zipMultipartFile.getIdentifier())
                        .eq(UploadUpInfo::getGroupId, zipMultipartFile.getGroupId())
                        .eq(UploadUpInfo::getUid, user.getId()));
                if (one == null) {
                    upInfo.setId(null);
                    upInfo.setGroupId(zipMultipartFile.getGroupId());
                    upInfo.setUid(user.getId());
                    upInfo.setIp(ToolUtil.getLocalIp(request));
                    upInfo.setCreateTime(DateUtil.getStrTime());
                    upInfo.setAnotherName(zipMultipartFile.getAnotherName());
                    uploadUpInfoService.save(upInfo);
                } else {
                    upInfo.setAnotherName(zipMultipartFile.getAnotherName());
                    one.setCreateTime(DateUtil.getStrTime());
                    uploadUpInfoService.updateById(one);
                }
                return Result.ok("上传成功", upInfo.getFileUrl());
            }
            //判断当前片段是否上传过
            List<Object> partInfos = redisUtil.lGet(UpLoadConstant.zipUploadInfo + zipMultipartFile.getIdentifier(), 0, -1);
            for (Object part : partInfos) {
                ZipPartInfo partInfo = (ZipPartInfo) part;
                if (partInfo.getPartNum() == zipMultipartFile.getChunkNumber()) {
                    return Result.ok();
                }
            }

            Integer chunkNum = null;

            //保存文件
            String partFilePath = FileUtil.saveZipPartFile(zipMultipartFile);
            if (partFilePath != null) {
                ZipPartInfo zipPartInfo = new ZipPartInfo();
                zipPartInfo.setPartNum(zipMultipartFile.getChunkNumber());
                zipPartInfo.setPartName(zipMultipartFile.getFileName());
                zipPartInfo.setPartPath(partFilePath);
                zipPartInfo.setPartSize(zipMultipartFile.getFileSize());
                //获取当前以及上传了多少片段
                chunkNum = setChunkNum(zipPartInfo, zipMultipartFile.getIdentifier());
            }

            //当最后一个片段上传后，发起合并操作
            if (chunkNum != null && chunkNum == zipMultipartFile.getTotalChunks()) {
                myAsyncService.mergePart(zipMultipartFile, ToolUtil.getLocalIp(request));
            }
            return Result.ok("上传成功,压缩文件处理中...", null);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return Result.ok();
    }

    public synchronized Integer setChunkNum(ZipPartInfo zipPartInfo, String identifier) {
        Integer chunkNum = (Integer) redisUtil.get(UpLoadConstant.uploadChunkNum + identifier);
        if (chunkNum == null) chunkNum = 0;
        redisUtil.lSet(UpLoadConstant.zipUploadInfo + identifier, zipPartInfo);
        redisUtil.set(UpLoadConstant.uploadChunkNum + identifier, ++chunkNum, cacheTime);
        return chunkNum;
    }


}
